package kuzmenkov.oip.controller.applet;

import java.applet.Applet;
import java.awt.BorderLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;

import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;

/**
 * Class is responsible for to create GUI of the image processing applet
 * 
 * @author Michael Kuzmenkov
 * 
 */
public class MainPanel extends JPanel {
	/**
	 * 
	 */
	private static final long serialVersionUID = 5928247689082445212L;

	/**
	 * A maximum length of a status message
	 */
	private static final int MAX_LENGTH_STATUS = 80;

	private static Logger log = Logger.getLogger(MainPanel.class.getName());

	/**
	 * A service which provides remote data
	 */
	private ClientService service;
	/**
	 * The parameters of the current module
	 */
	private List params;
	/**
	 * Appropriate images for the module
	 */
	private List images;

	/**
	 * A list of UI input components
	 */
	private List inputParamUICs;
	/**
	 * A list of UI output components
	 */
	private List outputParamUICs;

	private JComponent inputPanel;
	private JComponent outputPanel;
	/**
	 * A component which shows a status message
	 */
	private JLabel statusBar;

	/**
	 * An applet which contains this panel
	 */
	private Applet parentApplet;

	public MainPanel(Applet parent, ClientService service) throws Exception {
		this.service = service;
		this.parentApplet = parent;
		I18nTool.init(service);
		initData();
		initUI();
	}

	/**
	 * Initialize needed data for ui creating
	 */
	private void initData() {
		initParams();
		initImages();

	}

	/**
	 * Stores remote image data
	 */
	private void initImages() {
		List ims=null;
		try {
			ims = service.getRemoteImages();
		} catch (Exception e) {
			e.printStackTrace();
			printMessage(e.getMessage());
			images = new ArrayList();
			return;
		} 
		log.info("quantity of images: " + ims.size());
		images = new ArrayList(ims.size());

		for (int i = 0; i < ims.size(); i++) {
			Map p = (Map) ims.get(i);

			String filePath = (String) p.get(RemoteService.IMAGE_FILE_PATH);

			ImageData image = new ImageData();
			image.setFilePath(filePath);

			images.add(image);
		}

	}

	/**
	 * Stores remote parameter data
	 */
	private void initParams() {
		List ps;
		try {
			ps = service.getRemoteParameters();
		} catch (Exception e) {
			e.printStackTrace();
			printMessage(e.getMessage());
			params = new ArrayList();
			return;
		}
		I18nTool itool = I18nTool.getInstance();
		if (ps == null)
		    throw new IllegalStateException (itool.getMessage("MainPanel.hasNotReturnedParams"));
		log.info("quantity of params=" + ps.size());
		params = new ArrayList(ps.size());

		for (int i = 0; i < ps.size(); i++) {
			Map p = (Map) ps.get(i);
			Param param = new Param();
			String name = (String) p.get(RemoteService.PARAM_NAME);
			String defVal = (String) p.get(RemoteService.PARAM_DEFAULT_VALUE);
			boolean isResult = ((Boolean) p.get(RemoteService.PARAM_IS_RESULT))
					.booleanValue();
			String type = (String) p.get(RemoteService.PARAM_TYPE);
			String desc = (String) p.get(RemoteService.PARAM_DESCRIPTION);
			int position = ((Integer) p.get(RemoteService.PARAM_POSITION))
					.intValue();

			param.setName(name);
			param.setDefaultValue(defVal);
			param.setDescription(desc);
			param.setPosition(position);
			param.setResult(isResult);
			param.setType(type);

			params.add(param);
		}

	}

	/**
	 * Creates the GUI on the basis of the initialized data
	 * 
	 * @throws Exception
	 */
	private void initUI() throws Exception {
		I18nTool itool = I18nTool.getInstance();
		if (params == null || params.size() < 2) {
			log.severe("Params is not correct");
			throw new Exception(itool.getMessage("MainPanel.notCorrectParams", new Object[]{params}));
		}
		createStructure();
		addParams();

	}

	/**
	 * Adds appropriates parameter components to the main panel
	 */
	private void addParams() {
		inputParamUICs = new ArrayList();
		outputParamUICs = new ArrayList();

		ParamUICFactory factory = new ParamUICFactoryImpl(parentApplet, images);
		for (int i = 0; i < params.size(); i++) {
			Param param = (Param) params.get(i);
			ParamUIC p = factory.createParamComponent(param);

			if (param.isResult()) {
				addOutputParam(p);
			} else {
				addInputParam(p);
			}

		}

	}

	private void createStructure() {
		I18nTool itool = I18nTool.getInstance();

		setLayout(new GridBagLayout());

		inputPanel = Box.createVerticalBox();
		

		JPanel panel11 = new JPanel(new BorderLayout());
		panel11.setBorder(BorderFactory.createTitledBorder(itool
				.getMessage("inputParameterPanelTitle")));
		panel11.add(inputPanel, BorderLayout.NORTH);

		outputPanel = Box.createVerticalBox();
		JPanel panel12 = new JPanel(new BorderLayout());
		panel12.setBorder(BorderFactory.createTitledBorder(itool
				.getMessage("outputParameterPanelTitle")));
		panel12.add(outputPanel, BorderLayout.NORTH);

		statusBar = new JLabel();
		// pringMessage("Applet is running!");
		JButton process = new JButton(new PerformAction());

		GridBagConstraints c1 = createConstr(0, 0, 2, 1);
		c1.weightx = 100;
		c1.weighty = 100;
		c1.fill = GridBagConstraints.BOTH;


//		GridBagConstraints c11 = createConstr(0, 0, 1, 1);
//		c11.weightx = 100;
//		c11.weighty = 100;
//		c11.fill = GridBagConstraints.BOTH;
//		c11.anchor = GridBagConstraints.NORTHWEST;
//
//		GridBagConstraints c12 = createConstr(1, 0, 1, 1);
//		c12.weightx = 100;
//		c12.weighty = 100;
//		c12.fill = GridBagConstraints.BOTH;
//		c12.anchor = GridBagConstraints.NORTHWEST;

		GridBagConstraints c21 = createConstr(0, 1, 2, 1);
		c21.anchor = GridBagConstraints.WEST;
		c21.insets = new Insets(2, 2, 2, 2);

		GridBagConstraints c22 = createConstr(1, 1, 1, 1);
		c22.anchor = GridBagConstraints.EAST;
		c22.insets = new Insets(2, 2, 2, 2);

		JPanel panels = new JPanel(new GridLayout(1,2));
		panels.add(panel11);
		panels.add(panel12);
		
//		add(panel11, c11);
//		add(panel12, c12);
		
		add(panels,c1);
		add(statusBar, c21);
		add(process, c22);

	}

	/**
	 * Shows a given message as a status message
	 * 
	 * @param msg
	 *            a message
	 */
	private void printMessage(String msg) {
		if (statusBar==null){
			JOptionPane.showMessageDialog(null, msg);
			return;
		}
		statusBar.setToolTipText(null);
		if (msg == null) {
			msg = "";
		} else if (msg.length() > MAX_LENGTH_STATUS) {
			statusBar.setToolTipText(msg);
			msg = msg.substring(0, MAX_LENGTH_STATUS).trim() + "...";
		}

		statusBar.setText(msg);
	}

	private GridBagConstraints createConstr(int gridx, int gridy,
			int gridwidth, int gridheight) {
		GridBagConstraints constr = new GridBagConstraints();
		constr.gridx = gridx;
		constr.gridy = gridy;
		constr.gridwidth = gridwidth;
		constr.gridheight = gridheight;
		constr.fill = GridBagConstraints.NONE;

		return constr;
	}

	/**
	 * An action starts an image processing
	 * 
	 * @author Michael Kuzmenkov
	 * 
	 */
	class PerformAction extends AbstractAction {
		private static final long serialVersionUID = -339888476724316045L;

		public PerformAction() {
			I18nTool itool = I18nTool.getInstance();
			putValue(NAME, itool.getMessage("labelOfprocessButton"));
		}

		public void actionPerformed(ActionEvent e) {
			log.info("entry");
			I18nTool itool = I18nTool.getInstance();
			try {
				Object[] params = getInputParams();
				Object[] results = service.processRemotely(params);
				setOutputParams(results);
				printMessage("");
			} catch (Exception e1) {
				log.info("Error occurred: " + e.toString());
				printMessage(itool
						.getMessage(
								"MainPanel.unsuccessfulProcessing",
								new Object[] { e1.getMessage() }));
				e1.printStackTrace();
			}
		}

	}

	private void setOutputParams(Object[] results) throws Exception {
		I18nTool itool = I18nTool.getInstance();
		if (results == null || outputParamUICs.size() != results.length) {
			log.severe("Results are illegal: " + results);
			throw new Exception(itool
					.getMessage("MainPanel.illegalResult"));
		}
		for (int i = 0; i < results.length; i++) {
			ParamUIC paramUIC = (ParamUIC) outputParamUICs.get(i);
			paramUIC.setValue(results[i]);
		}
	}

	private Object[] getInputParams() throws Exception {
		I18nTool itool = I18nTool.getInstance();
		if (params.size() != inputParamUICs.size() + outputParamUICs.size()) {
			String err = itool.getMessage(
					"MainPanel.illegalState", new Object[] {
							new Integer(params.size()),
							new Integer(inputParamUICs.size()),
							new Integer(outputParamUICs.size()) });

			log.severe(err);
			throw new IllegalStateException(err);
		}

		Object[] ps = new Object[inputParamUICs.size()];

		for (int i = 0; i < ps.length; i++) {
			ParamUIC paramUIC = (ParamUIC) inputParamUICs.get(i);
			Object objVal = paramUIC.getValue();

			if (objVal == null) {
				throw new Exception(itool.getMessage(
						"MainPanel.allRequired",
						new Object[] { new Integer(i + 1) }));
			}

			ps[i] = convert(objVal, ((Param)params.get(i)).getType());
		}

		return ps;
	}

	/**
	 * Converts the given value to a appropriate type
	 * 
	 * @param val
	 * @return
	 */
	private Object convert(Object val, String type) throws Exception {
		Object _old = val;
		if (Param.INTEGER_TYPE.equals(type)){
			if (! (val instanceof Integer) ){
				val = new Integer (val.toString());
			}
		} else if (Param.FLOAT_TYPE.equals(type)){
			if (!(val instanceof Float)) {
				val = new Float(val.toString());
			}
		}
		log.info("Type "+_old.getClass() + " of value "+_old+" has been converted to "+val.getClass()+" (type="+type+")");
		return val;
	}

	/**
	 * Adds the input param component to the main panel
	 * 
	 * @param component
	 */
	private void addInputParam(ParamUIC component) {
		I18nTool itool = I18nTool.getInstance();
		if (component == null) {
			log.severe("Component is null");
			throw new IllegalArgumentException(
					itool.getMessage("MainPanel.nullComponent"));
		}
		inputParamUICs.add(component);

		inputPanel.add(component);
	}

	/**
	 * Adds the output param component to the main panel
	 * 
	 * @param component
	 */
	private void addOutputParam(ParamUIC component) {
		I18nTool itool = I18nTool.getInstance();
		if (component == null) {
			log.severe("Component is null");
			throw new IllegalArgumentException(itool.getMessage("MainPanel.nullComponent"));
		}
		outputParamUICs.add(component);

		outputPanel.add(component);
	}

}
